package net.osc.service.job;

import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.Date;
import java.util.List;

import net.osc.model.job.CommonJobDetail;
import net.osc.service.BaseManager;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.CronExpression;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.StatefulJob;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("commonJobManager")
@Transactional(readOnly = true)
public class CommonJobManagerImpl extends BaseManager implements CommonJobManager,
		ApplicationContextAware {

	private static final Log logger = LogFactory.getLog(CommonJobManagerImpl.class);

	public static final String BEAN_NAME_KEY = "bean_name_key";
	public static final String BEAN_METHOD_KEY = "bean_method_key";
	public static final String JOB_KEY = "job_key";
	public static final String JOB_MANAGER_KEY = "job_manager_key";
	public static final String APPLICATION_CONTEXT = "application_context";

	private ApplicationContext applicationContext;

	public CommonJobDetail getJobById(Long id) {
		return hibernateDao.get(CommonJobDetail.class, id);
	}

	@Transactional
	public void activeJob(CommonJobDetail job) {
		if (job.getId() == null) {
			hibernateDao.save(job);
		}
		try {
			JobDetail jobDetail = createJobDetail(job);
			JobDetail oldJobDetail = getJobDetail(job.getJobName());
			if (oldJobDetail != null) {
				logger.info("任务已存在,任务=" + job.getJobName());
				disActiveJob(job);
			}
			Trigger trigger = createTrigger(job);
			getScheduler().scheduleJob(jobDetail, trigger);
			job.setIsActive(true);
			updateJobByTrigger(job, trigger);
			if (!getScheduler().isStarted()) {
				getScheduler().start();
			}
		} catch (Exception e) {
			throw new RuntimeException("任务启动失败,任务=" + job.getJobName() + " " + e.getMessage(), e);
		}
		logger.info("任务启动成功,任务=" + job.getJobName());
	}

	@Transactional
	public void disActiveJob(CommonJobDetail job) {
		try {
			getScheduler().deleteJob(job.getJobName(), Scheduler.DEFAULT_GROUP);
			job.setIsActive(false);
			job.setNextFireTime(null);
			hibernateDao.save(job);
		} catch (Exception e) {
			throw new RuntimeException("任务停止失败,任务=" + job.getJobName() + " " + e.getMessage(), e);
		}
		logger.info("任务停止成功,任务=" + job.getJobName());
	}

	public static class QuartzJobBean implements Job {

		public void execute(JobExecutionContext context) throws JobExecutionException {
			String beanName = (String) context.getMergedJobDataMap().get(
					CommonJobManager.BEAN_NAME_KEY);
			String beanMethod = (String) context.getMergedJobDataMap().get(
					CommonJobManager.BEAN_METHOD_KEY);
			CommonJobDetail job = (CommonJobDetail) context.getMergedJobDataMap().get(
					CommonJobManager.JOB_KEY);
			CommonJobManagerImpl jobManager = (CommonJobManagerImpl) context.getMergedJobDataMap()
					.get(CommonJobManager.JOB_MANAGER_KEY);
			ApplicationContext applicationContext = (ApplicationContext) context
					.getMergedJobDataMap().get(CommonJobManager.APPLICATION_CONTEXT);

			if (StringUtils.isEmpty(beanName) || StringUtils.isEmpty(beanMethod)
					|| applicationContext == null || job == null || jobManager == null) {
				throw new JobExecutionException("任务参数不全,任务=" + job.getJobName());
			}
			Object bean = applicationContext.getBean(beanName);
			try {
				Method method = bean.getClass().getMethod(beanMethod);
				Object result = method.invoke(bean);
				context.setResult(result);
				jobManager.updateJobByTrigger(job, context.getTrigger());
			} catch (Exception e) {
				throw new JobExecutionException(e.getMessage(), e);
			}
		}

	}

	private Scheduler getScheduler() {
		try {
			return StdSchedulerFactory.getDefaultScheduler();
		} catch (SchedulerException e) {
			throw new RuntimeException("获取调度器失败", e);
		}
	}

	protected CronTrigger getTrigger(String triggerName) {
		try {
			return (CronTrigger) getScheduler().getTrigger(triggerName, Scheduler.DEFAULT_GROUP);
		} catch (SchedulerException e) {
			throw new RuntimeException("获取触发器失败,触发器=" + triggerName, e);
		}
	}

	protected JobDetail getJobDetail(String jobName) {
		try {
			return getScheduler().getJobDetail(jobName, Scheduler.DEFAULT_GROUP);
		} catch (SchedulerException e) {
			throw new RuntimeException("获取任务详细失败,任务=" + jobName, e);
		}
	}

	protected JobDetail createJobDetail(CommonJobDetail job) {
		Class<?> jobClass = (job.getIsConcurrent() ? QuartzJobBean.class
				: StatefulQuartzJobBean.class);
		JobDetail jobDetail = new JobDetail(job.getJobName(), Scheduler.DEFAULT_GROUP, jobClass);
		jobDetail.getJobDataMap().put(CommonJobManagerImpl.BEAN_NAME_KEY, job.getBeanName());
		jobDetail.getJobDataMap().put(CommonJobManagerImpl.BEAN_METHOD_KEY, job.getMethodName());
		jobDetail.getJobDataMap().put(CommonJobManagerImpl.JOB_KEY, job);
		jobDetail.getJobDataMap().put(CommonJobManagerImpl.JOB_MANAGER_KEY, this);
		jobDetail.getJobDataMap().put(CommonJobManagerImpl.APPLICATION_CONTEXT,
				this.applicationContext);
		return jobDetail;
	}

	public CronTrigger createTrigger(CommonJobDetail job) {
		CronTrigger trigger = new CronTrigger(job.getTriggerName(), Scheduler.DEFAULT_GROUP);
		CronExpression cron;
		try {
			cron = new CronExpression(job.getCronExpr());
		} catch (ParseException e) {
			throw new RuntimeException("定时表达式不合法,任务=" + job.getJobName() + " ,定时表达式="
					+ job.getCronExpr(), e);
		}
		Date now = new Date();
		Date startTime = job.getStartTime();
		if (startTime == null) {
			startTime = now;
		}
		Date after = now.compareTo(startTime) > 0 ? now : startTime;
		trigger.setCronExpression(cron);
		trigger.setStartTime(cron.getNextValidTimeAfter(after));
		trigger.setEndTime(job.getEndTime());
		return trigger;
	}

	public static class StatefulQuartzJobBean extends QuartzJobBean implements StatefulJob {

	}

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
		logger.info("启动活动的定时任务开始");
		List<CommonJobDetail> jobs = hibernateDao
				.find("from CommonJobDetail job where job.isActive=true");
		for (CommonJobDetail job : jobs) {
			activeJob(job);
		}
		logger.info("启动活动的定时任务结束");
	}

	@Transactional
	public void updateJobByTrigger(CommonJobDetail job, Trigger trigger) {
		job.setNextFireTime(trigger.getNextFireTime());
		job.setPreviousFireTime(trigger.getPreviousFireTime());
		if (null == job.getNextFireTime()) {
			logger.info("任务没有再次执行的机会,自动转成非活动状态,任务=" + job.getJobName());
			disActiveJob(job);
			return;
		}
		hibernateDao.save(job);
	}

	@Transactional
	public void saveJob(CommonJobDetail job) {
		try {
			new CronExpression(job.getCronExpr());
		} catch (ParseException e) {
			throw new RuntimeException("定时表达式不合法, 表达式=" + job.getCronExpr(), e);
		}
		if (job.getIsActive()) {
			activeJob(job);
			return;
		}
		hibernateDao.save(job);
	}

	@Transactional
	public void delJobs(Long[] ids) {
		for (Long id : ids) {
			CommonJobDetail job = getJobById(id);
			if (job.getIsActive()) {
				disActiveJob(job);
			}
			hibernateDao.delete(job);
		}
	}

}
